#include "common.h"

off_t DS_Atoof(u_char *line, size_t n)
{
    off_t  value;

    if (n == 0) {
        return DS_ERROR;
    }

    for (value = 0; n--; line++) {
        if (*line < '0' || *line > '9') {
            return DS_ERROR;
        }

        value = value * 10 + (*line - '0');
    }

    if (value < 0) {
        return DS_ERROR;

    } else {
        return value;
    }
}


Buf * DS_CreateTempBuf(ds_pool_s *pool, size_t size)
{
    Buf *b;

    b = DS_Pcalloc(pool, sizeof(Buf));
    if (b == NULL) {
        return NULL;
    }

    b->start = DS_Palloc(pool, size);
    if (b->start == NULL) {
        return NULL;
    }

    b->pos = b->start;
    b->last = b->start;
    b->end = b->last + size;

    return b;
}


Chain * DS_AllocChainLink(ds_pool_s *pool)
{
    Chain  *cl;

    cl = pool->chain;

    if (cl) {
        pool->chain = cl->next;
        return cl;
    }
    
    cl = DS_Palloc(pool, sizeof(Chain));
    if (cl == NULL) {
        return NULL;
    }

    return cl;
}

ds_array_s * DS_ArrayCreate(ds_pool_s *p, unsigned int n, size_t size)
{
    ds_array_s *a;

    a = DS_Palloc(p, sizeof(ds_array_s));
    if (a == NULL) {
        return NULL;
    }

    a->elts = DS_Palloc(p, n * size);
    if (a->elts == NULL) {
        return NULL;
    }

    a->nelts = 0;
    a->size = size;
    a->nalloc = n;
    a->pool = p;

    return a;
}


void DS_ArrayDestroy(ds_array_s *a)
{
    ds_pool_s  *p;

    p = a->pool;

    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {
        p->d.last -= a->size * a->nalloc;
    }

    if ((u_char *) a + sizeof(ds_array_s) == p->d.last) {
        p->d.last = (u_char *) a;
    }
}


void *DS_ArrayPush(ds_array_s *a)
{
    void        *elt, *new;
    size_t       size;
    ds_pool_s  *p;

    if (a->nelts == a->nalloc) {

        size = a->size * a->nalloc;

        p = a->pool;

        if ((u_char *) a->elts + size == p->d.last
            && p->d.last + a->size <= p->d.end)
        {
            p->d.last += a->size;
            a->nalloc++;

        } else {

            new = DS_Palloc(p, 2 * size);
            if (new == NULL) {
                return NULL;
            }

            memcpy(new, a->elts, size);
            a->elts = new;
            a->nalloc *= 2;
        }
    }

    elt = (u_char *) a->elts + a->size * a->nelts;
    a->nelts++;

    return elt;
}


void *DS_PallocBlock(ds_pool_s *pool, size_t size);
void *DS_PallocLarge(ds_pool_s *pool, size_t size);

void * DS_Alloc(size_t size)
{
    void  *p;

    p = malloc(size);
    if (p == NULL) {
        return NULL;     
    }
    return p;
}

ds_pool_s * DS_CreatePool(size_t size)
{
    ds_pool_s  *p;

    p = DS_Alloc(size);
    if (p == NULL) {
        return NULL;
    }

    p->d.last = (u_char *) p + sizeof(ds_pool_s);
    p->d.end = (u_char *) p + size;
    p->d.next = NULL;
    p->d.failed = 0;

    size = size - sizeof(ds_pool_s);
    p->max = (size < DS_MAX_ALLOC_FROM_POOL) ? size : DS_MAX_ALLOC_FROM_POOL;

    p->current = p;
    p->large = NULL;
    p->chain = NULL;
    
    return p;
}


void DS_DestroyPool(ds_pool_s *pool)
{
    ds_pool_s          *p, *n;
    ds_pool_large_s    *l;

    for (l = pool->large; l; l = l->next) {
       if (l->alloc) {
            free(l->alloc);
        }
    }

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        free(p);

        if (n == NULL) {
            break;
        }
    }
}

void * DS_Palloc(ds_pool_s *pool, size_t size)
{
    u_char      *m;
    ds_pool_s  *p;
/*
    if (g_server && g_server->log && g_server->log->fd > 0) {
	    Log_Write(g_server->log, "Alloc %d bytes", size);
    }
*/
    if (size <= pool->max) {

        p = pool->current;

        do {
            m = align_ptr(p->d.last, DS_ALIGNMENT);

            if ((size_t) (p->d.end - m) >= size) {
                p->d.last = m + size;

                return m;
            }

            p = p->d.next;

        } while (p);

        return DS_PallocBlock(pool, size);
    }

    return DS_PallocLarge(pool, size);
}

void * DS_PallocBlock(ds_pool_s *pool, size_t size)
{
    u_char      *m;
    size_t       psize;
    ds_pool_s  *p, *newer, *current;

    psize = (size_t) (pool->d.end - (u_char *) pool);

    m = DS_Alloc(psize);
    if (m == NULL) {
        return NULL;
    }
    
    newer = (ds_pool_s *) m;

    newer->d.end = m + psize;
    newer->d.next = NULL;
    newer->d.failed = 0;

    m += sizeof(ds_pool_data_s);
    m = align_ptr(m, DS_ALIGNMENT);
    newer->d.last = m + size;

    current = pool->current;

    for (p = current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            current = p->d.next;
        }
    }

    p->d.next = newer;

    pool->current = current ? current : newer;

    return m;
}


void * DS_PallocLarge(ds_pool_s *pool, size_t size)
{
    void              *p;
    unsigned int         n;
    ds_pool_large_s  *large;

    p = DS_Alloc(size);
    if (p == NULL) {
        return NULL;
    }

    n = 0;

    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }

        if (n++ > 3) {
            break;
        }
    }

    large = DS_Palloc(pool, sizeof(ds_pool_large_s));
    if (large == NULL) {
        free(p);
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}


void * DS_Pcalloc(ds_pool_s *pool, size_t size)
{
    void *p;

    p = DS_Palloc(pool, size);
    if (p) {
        memset(p, 0, size);
    }

    return p;
}

